/*
 * CommonApp: version-0.0.1a
 *
 * Change Logs:
 * Date               Author              Notes
 * 2023-2-12          chenjiaxing         first version
 *
 * Description: Common application for network device, this code implements 
 *              the common operations for any device(server in the network), used for all link type.
 */
#include <string.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include "libhash.h"
#include "device_def.h"
#include "config.h"
#include "zlog.h"

devInfo_t devInfoArray[20];

int addDev(unsigned char devId, devInfo_t devInfo)
{
  int ret = -1;
  if(devId == 0){
    return -1;
  }
  unsigned char devIndex = devId - 1;
  memcpy(&devInfoArray[devIndex], &devInfo, sizeof(devInfo_t));
  devInfoArray[devIndex].status = VALID;

  linkCtlInfo_t linkServerCtl = {
    .server.port    = devInfo.devNetInfo.devEthLinkInfo.port,
    .server.linkId  = devId,
    .linkCtlType    = ADD_LINK_CTL,
  };
  memcpy(linkServerCtl.server.ipAddr, devInfo.devNetInfo.devEthLinkInfo.ipAddr, sizeof(devInfo.devNetInfo.devEthLinkInfo.ipAddr));
  ret = tcpDevConnectCtl(linkServerCtl, parseDevCmd); 
  if(ret < 0){
    zlog_error(gZlog, "fail to connect server ip[%s], port[%d]", linkServerCtl.server.ipAddr, linkServerCtl.server.port);
    return -1;
  }
  return 0;
}

int getDev(unsigned char devId, devInfo_t *devInfo)
{
  if(devId == 0){
    return -1;
  }
  unsigned char devIndex = devId - 1;
  memcpy(devInfo, &devInfoArray[devIndex], sizeof(devInfo_t));
  return 0;
}

int delDev(unsigned char devId)
{
   if(devId == 0){
    return -1;
  }
  unsigned char devIndex = devId - 1;
  devInfoArray[devIndex].status = INVALID;
  return 0; 
}

int printDev(void)
{
  int ret = 0;
  for(int i = 0; i < sizeof(devInfoArray) / sizeof(devInfo_t); i++){
    if(devInfoArray[i].status >= VALID){
      ret ++;
      zlog_info(gZlog, "get acqNode[%d] Info:       \n\
          boardName[%s]                             \n\
          fwVersion[%s]                             \n\
          ipAddr[%s]                                \n\
          mask[%s]                                  \n\
          gateway[%s]                               \n\
          mac[%s]                                   \n\
          port[%d]                                  \n\
          ret[%d]",                                           \
          devInfoArray[i].devId,                              \
          devInfoArray[i].devHwInfo.devBoardName,             \
          devInfoArray[i].devHwInfo.devFwInfo,                \
          devInfoArray[i].devNetInfo.devEthLinkInfo.ipAddr,   \
          devInfoArray[i].devNetInfo.devEthLinkInfo.mask,     \
          devInfoArray[i].devNetInfo.devEthLinkInfo.gateway,  \
          devInfoArray[i].devNetInfo.devEthLinkInfo.mac,      \
          devInfoArray[i].devNetInfo.devEthLinkInfo.port,     \
          ret);
    } 
  }
  return ret;
}

int clearDev(void)
{
  int ret = 0;
  for(int i = 0; i < sizeof(devInfoArray) / sizeof(devInfo_t); i++){
    if(devInfoArray[i].status >= VALID){
      ret ++;
      devInfoArray[i].status = INVALID;
      zlog_info(gZlog, "delete acqNode[%d] Info:  \n\
          boardName[%s]                             \n\
          fwVersion[%s]                             \n\
          ipAddr[%s]                                \n\
          mask[%s]                                  \n\
          gateway[%s]                               \n\
          mac[%s]                                   \n\
          port[%d]                                  \n\
          ret[%d]",                                           \
          devInfoArray[i].devId,                              \
          devInfoArray[i].devHwInfo.devBoardName,             \
          devInfoArray[i].devHwInfo.devFwInfo,                \
          devInfoArray[i].devNetInfo.devEthLinkInfo.ipAddr,   \
          devInfoArray[i].devNetInfo.devEthLinkInfo.mask,     \
          devInfoArray[i].devNetInfo.devEthLinkInfo.gateway,  \
          devInfoArray[i].devNetInfo.devEthLinkInfo.mac,      \
          devInfoArray[i].devNetInfo.devEthLinkInfo.port,     \
          ret);
    } 
  }
  return ret;
}

static hash_table protocolTable;

/* BASE OPCODE1:0x01~0x03, SPEC OPCODE1:0x04~0x05 */
int protocolHashFunc(key_type key)
{
  return key;
}

void registerOpcodeOperation(int opcode1, proFun_t fun) 
{
	if(fun == NULL) {
		zlog_error(gZlog, "the function is empty");
		return;
	}
	struct operation *opt = (struct operation*)malloc(sizeof(operation_t));
	if(!opt) {
		zlog_error(gZlog, "register opcode1[%d] failed\n\r", opcode1);
		return;
	}
	opt->fun = fun;
	/* hash insert */
	int ret = hash_insert(&protocolTable, opcode1, opt);
	if(ret < 0)
		zlog_error(gZlog, "register opcode1[%#4x] function:%p............[ failed ]\n\r", opcode1, fun);
	else
		zlog_error(gZlog, "register opcode1[%#4x] function:%p............[ success ]\n\r", opcode1, fun);
}

void unregisterOpcode(int opcode1)
{
	struct operation *opt;
	
	opt = hash_find(&protocolTable, opcode1);
	if(!opt)
		return;
	else{
		hash_remove(&protocolTable, opcode1);
		free(opt);
	}
	return;
}

struct operation* findOperation(int opcode1)
{
	struct operation *opt;
	
	opt = hash_find(&protocolTable, opcode1);
	if(!opt)
		return NULL;
	else
		return opt;
}

devMsg_t gDevMsg;
int parseDevCmd(void *id, char *payload, int size, 
    int op1, int op2, int errcode, struct frame_list *frames)
{
  ProChan *chan = id;
  zlog_debug(gZlog, "server message received, opcode1[0x%x], opcode2[0x%x]\n", op1, op2);
  /* copy msg */
  gDevMsg.chanId  = chan->chanid;
  gDevMsg.op1     = op1;
  gDevMsg.op2     = op2;
  gDevMsg.errCode = errcode;
  if(payload){
    memcpy(gDevMsg.payload, payload, size);
  }
  /* release block */
  ackFlag = true;
  /*
  pthread_mutex_lock(&gRequirRespondmutex);
	pthread_cond_signal(&gRequirRespondCond);
	pthread_mutex_unlock(&gRequirRespondmutex);
  */
  return 0;
}

pthread_cond_t  gRequirRespondCond;
pthread_mutex_t gRequirRespondmutex;
bool ackFlag = false;
int devProtocolInit(void)
{
  ackFlag = false;
  /*
  if(pthread_mutex_init(&gRequirRespondmutex, NULL)){
    zlog_error(gZlog, "gRequirRespondmutex creat failed");
        return -1;
  }
  if(pthread_cond_init(&gRequirRespondCond, NULL)) {
    zlog_error(gZlog, "gRequirRespondCond creat failed");
    return -1;
  }
  */
  return 0;
}

int devProtocolRelease(void)
{
  pthread_cond_destroy(&gRequirRespondCond);
  pthread_mutex_destroy(&gRequirRespondmutex);
  memset(&gDevMsg, 0, sizeof(devMsg_t));
  return 0;
}

int waitAck(unsigned int timeoutMs)
{
  int ret = -1;
  unsigned int timeMsCnt = 0;
  while(!ackFlag){
    timeMsCnt ++;
    if(timeMsCnt >= timeoutMs){
      break;
    }
    usleep(1000);
  }
  ackFlag = false;
  /*
  unsigned int timeoutSec  = timeoutMs / 1000;
  unsigned int timeoutUsec = timeoutMs % 1000;
	struct timeval now;
	struct timespec outtime;
	
	pthread_mutex_lock(&gRequirRespondmutex);
	gettimeofday(&now, NULL);         
	outtime.tv_sec = now.tv_sec + timeoutSec;
	outtime.tv_nsec = now.tv_usec * 1000 + timeoutMs * 1000000;
	
	ret = pthread_cond_timedwait(&gRequirRespondCond, &gRequirRespondmutex, &outtime);
	if (ret != 0){
		zlog_error(gZlog, "timeout");
		return -1;
	}
	pthread_mutex_unlock(&gRequirRespondmutex);
	*/
  return 0;
}

int requirResetDev(int devId)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //1. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), NULL, 0, \
          BASE_OP1_RESET_DEV, 0, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to reset device[%d], ret[%d]", devId, ret);
    return ret;
  }
  //2. 等待响应帧
  ret = waitAck(ACK_TIMEOUT_MS);
  if(ret < 0){
    zlog_error(gZlog, "reset device[%d] ack timeout", devId);
    return ret;
  }
  //3. 处理接收内容
  if(/* gDevMsg.chanId != devId || */gDevMsg.op1 != BASE_OP1_RESET_DEV){
    zlog_error(gZlog, "recv reset device[%d] ack failed", devId);
    return ret;
  }
  return 0;
}

int requirSyncTime(int devId)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //0. 准备payload
  struct timeval now, timeStamp;
  gettimeofday(&now, NULL);
  timeStamp.tv_sec  = htonl(now.tv_sec);
  timeStamp.tv_usec = htonl(now.tv_usec);
  //1. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), (char *)&timeStamp, sizeof(struct timeval), \
          BASE_OP1_SYNC_TIME, 0, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to sync time to device[%d], ret[%d]", devId, ret);
    return ret;
  }
  //2. 等待响应帧
  ret = waitAck(ACK_TIMEOUT_MS);
  if(ret < 0){
    zlog_error(gZlog, "reset device[%d] ack timeout", devId);
    return ret;
  }
  //3. 处理接收内容
  if(/* gDevMsg.chanId != devId || */gDevMsg.op1 != BASE_OP1_SYNC_TIME){
    zlog_error(gZlog, "recv sync time to device[%d] ack failed", devId);
    return ret;
  }
  return 0;
}

int requirChkAlive(int devId)
{
  int ret = -1;
  linkList_t *linkNode = NULL;
  linkNode = findLinkNodeFromList(gServerLinkHead, devId);
  //1. 发送命令帧
  ret = proFramesSend(&(linkNode->link.chan), NULL, 0, \
          BASE_OP1_CHECK_ALIEVE, 0, 0);
  if(ret < 0){
    zlog_error(gZlog, "failed to check alive device[%d], ret[%d]", devId, ret);
    return ret;
  }
  //2. 等待响应帧
  ret = waitAck(ACK_TIMEOUT_MS);
  if(ret < 0){
    zlog_error(gZlog, "check alive device[%d] ack timeout", devId);
    return ret;
  }
  //3. 处理接收内容
  if(/* gDevMsg.chanId != devId || */gDevMsg.op1 != BASE_OP1_CHECK_ALIEVE){
    zlog_error(gZlog, "recv check alive device[%d] ack failed", devId);
    return ret;
  }
  return 0;
}

int requirGetHwInfo(int devId, hwInfo_t *hwInfo)
{
  return 0;
}

int requirGetEthInfo(int devId, ethInfo_t *ethInfo)
{
  return 0;
}

int requirSetEthInfo(int devId, ethInfo_t ethInfo)
{
  return 0;
}
